using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using IndieStudio.BugsBang.Utility;

///Developed By Indie Studio
///https://assetstore.unity.com/publishers/9268
///www.indiestd.com
///info@indiestd.com

#pragma warning disable 0168 // variable declared but not used.

namespace IndieStudio.BugsBang.Game
{
	[DisallowMultipleComponent]
	public class GameManager : MonoBehaviour
	{
		/// <summary>
		/// The level text.
		/// </summary>
		public Text levelText;

		/// <summary>
		/// The score text.
		/// </summary>
		public Text scoreText;

		/// <summary>
		/// The score value of the current level.
		/// </summary>
		[HideInInspector]
		public int levelScore;

		/// <summary>
		/// The current lives for the user.
		/// </summary>
		[HideInInspector]
		public int currentLives;

		/// <summary>
		/// The destroy effects list includes ray, smash effects.
		/// </summary>
		public GameObject[] destroyEffects;

		/// <summary>
		/// The score bonus effect.
		/// </summary>
		public GameObject scoreBonusEffect;

		/// <summary>
		/// The score penalty effect.
		/// </summary>
		public GameObject scorePenaltyEffect;

		/// <summary>
		/// The parent of finger prin.
		/// </summary>
		public Transform fingerPrintParent;

		/// <summary>
		/// Used to follow an object/bug in the screen
		/// </summary>
		public TargetHint targetHint;

		/// <summary>
		/// Whether the mouse click is down or not.
		/// </summary>
		private bool mouseClickDown;

		/// <summary>
		/// A temp vector used for user click position.
		/// </summary>
		private Vector3 tempClickPosition;

		/// <summary>
		/// Your target dialog reference.
		/// </summary>
		public Dialog yourTargetDialog;

		/// <summary>
		/// The right panel transform reference.
		/// </summary>
		public Transform rightPanel;

		/// <summary>
		/// The parent of grounded objects.
		/// </summary>
		public Transform groundObjectsParent;

		/// <summary>
		/// The parent of path objects of type 'FLY'.
		/// </summary>
		public Transform flyObjectsParent;

		/// <summary>
		/// The parent of effects.
		/// </summary>
		public Transform effectsParent;

		/// <summary>
		/// The parent of the path (Paths).
		/// </summary>
		public Transform pathParent;

		/// <summary>
		/// The lives image in the top panel.
		/// </summary>
		public Image livesImage;

		/// <summary>
		/// The booster sprites in order [Live,Bang,Time].
		/// </summary>
		public Sprite[] boosterSprites;

		/// <summary>
		/// The targets score list.
		/// Each time you hit a path object, the fill amount of the cover of eacb score image will be decreased
		/// </summary>
		[HideInInspector]
		public List<TargetScore> targetsScore = new List<TargetScore> ();

		/// <summary>
		/// Whether the GameLoop is running or not.
		/// </summary>
		private bool isRunning;

		/// <summary>
		/// A static instance of this class.
		/// </summary>
		public static GameManager instance;

		void Awake ()
		{
			//Set up the static instance of this class
			if (instance == null) {
				instance = this;
			}
		}

		void Start ()
		{
			//Setting up the references

			if (yourTargetDialog == null) {
				yourTargetDialog = GameObject.Find ("YourTargetDialog").GetComponent<Dialog> ();
			}

			if (groundObjectsParent == null) {
				Debug.LogWarning ("Ground Objects Parent is not defined in GameManager component");
			}
			if (flyObjectsParent == null) {
				Debug.LogWarning ("Fly Objects Parent is not defined in GameManager component");
			}

			targetsScore.Clear ();

			if (LevelsManager.instance.GetCurrentLevel () == null) {
				Debug.LogWarning ("You have not select any level");
				return;
			}

			Screen.sleepTimeout = SleepTimeout.NeverSleep;

			StartCoroutine ("Init");
		}

		/// <summary>
		/// Initialize the game coroutine
		/// </summary>
		private IEnumerator Init ()
		{

			//Set the title of the level
			SetLevelTitleText ();

			yourTargetDialog.transform.localScale = Vector3.one;

			//Set level score in the UI
			levelScore = LevelsManager.instance.GetCurrentLevel ().score;
			SetScoreText (levelScore);

			//Set Level lives in the UI
			currentLives = LevelsManager.instance.GetCurrentLevel ().lives;
			SetLivesFillValue (currentLives);

			//Create Target UI for dialog and score
			Transform targetsInDialog = yourTargetDialog.transform.Find ("Targets");

			//Create Targets UI
			for (int i = 0; i < LevelsManager.instance.GetCurrentLevel ().targets.Count; i++) {
				PathObject po = LevelsManager.instance.GetCurrentLevel ().targets [i].pathObject;

				//Create targets ui in the right panel
				Transform targetScore = CommonUtil.CreateTargetUI (PrefabsManager.instance.targetScorePrefab, rightPanel, po.gameObject);

				//Add the target score component to the targets score list
				targetsScore.Add (targetScore.GetComponent<TargetScore> ());
				//Set max value of the target score
				targetScore.GetComponent<TargetScore> ().SetMaxValue (LevelsManager.instance.GetCurrentLevel ().targets [i].count);

				//Create targets ui in your targets dialog
				Transform targetStage = CommonUtil.CreateTargetUI (PrefabsManager.instance.targetStagePrefab, targetsInDialog, po.gameObject);
				targetStage.Find ("Counter").Find ("Text").GetComponent<Text> ().text = LevelsManager.instance.GetCurrentLevel ().targets [i].count.ToString ();
			}

			//Display targets to the user
			yourTargetDialog.Show (false);

			//Wait 4 seconds , let the user see the targets
			yield return new WaitForSeconds (4f);

			//Hide Display targets
			yourTargetDialog.Hide (false);

			//Create the paths of the current level
			foreach (LevelsManager.Level.Path path in LevelsManager.instance.GetCurrentLevel().paths) {
				GameObject pathGameObject = Instantiate (path.value.gameObject, path.value.transform.position, Quaternion.identity) as GameObject;
				//Set path parent
				if (pathParent != null)
					pathGameObject.transform.SetParent (pathParent);

				//Get path component
				Path pathComponent = pathGameObject.GetComponent<Path> ();

				//Add current path to paths list
				Paths.instance.paths.Add (pathComponent);

				//Add the path objects from levels manager to the Path component
				foreach (PathObject po in path.pathObjects) {
					pathComponent.pathObjects.Add (po);
				}
				pathComponent.Run ();
			}

			if (pathParent != null) {
				//Scale the paths based on the resolution of the screen
				pathParent.GetComponent<TransformScale> ().Run ();
			}

			//Set user ide after n seconds
			Invoke ("OnUserBecomesIdle", 5);

			//Set game running true
			isRunning = true;
		}

		/// <summary>
		/// When the GameObject becomes invisible.
		/// </summary>
		void OnDisable ()
		{
			//Stop the timer on disable this gameobject
			if (Timer.instance != null)
				Timer.instance.Stop ();
		}

		/// <summary>
		/// On destory GameObject.
		/// </summary>
		void OnDestroy ()
		{
			Screen.sleepTimeout = SleepTimeout.SystemSetting;
			Cursor.instance.DisableTrails ();
		}

		// Update is called once per frame
		void Update ()
		{
			//Invoke game loop method
			GameLoop ();
		}

		/// <summary>
		/// The Game's loop.
		/// </summary>
		private void GameLoop ()
		{
			//TODO: Add infinite level option
			//TODO: Add two buttons for campain , free

            if (Application.isMobilePlatform)
            {//Mobile Platform such as IOS/Android
                foreach (Touch touch in Input.touches)
                {
                    if (touch.phase == TouchPhase.Began)
                    {
                        OnClickBegan(touch.position);
                    }
                    else if (touch.phase == TouchPhase.Moved)
                    {
                        OnClickMoved(touch.position);
                    }
                    else if (touch.phase == TouchPhase.Ended)
                    {
                        OnClickEnded();
                    }
                }
            }
            else
            {//Other Platforms
                if (Input.GetMouseButtonDown(0))//On click down
                {
                    mouseClickDown = true;
                    OnClickBegan(Input.mousePosition);
                }
                else if (Input.GetMouseButtonUp(0))//On click up
                {
                    mouseClickDown = false;
                    OnClickEnded();
                }

                if (mouseClickDown)
                {
                    OnClickMoved(Input.mousePosition);
                }
            }
		}


        /// <summary>
        /// On click/touch began
        /// </summary>
        /// <param name="pos"></param>
        private void OnClickBegan(Vector3 pos)
        {
            //Reset temp click postion to postion value out of screen , to allow first click by user
            tempClickPosition = Vector3.one * 10000;

            //Enable the trail of the cursor/touch
            Cursor.instance.EnableTrail(0);

            //Set user active
            CancelInvoke("OnUserBecomesIdle");

            OnUserBecomesActive();

            if (IsValidRayCast(pos))
            {
                if (!RayCast(pos)){//if not clicked on a collider
                    //Create new finger print effect
                    CreateFingerPrint(pos);
                }
            }
        }

        /// <summary>
        /// On click/touch moved
        /// </summary>
        /// <param name="pos"></param>
        private void OnClickMoved(Vector3 pos)
        {
            RayCast(pos);
        }

        /// <summary>
        /// On click/touch ended
        /// </summary>
        private void OnClickEnded()
        {
            //Set user idle after 3 seconds
            Invoke("OnUserBecomesIdle", 3);

            //Disable the trail of the cursor/touch
            Cursor.instance.DisableTrail(0);
        }

		/// <summary>
		/// Raycast the click (touch) on the screen.
		/// </summary>
		/// <param name="clickPosition">The position of the click/touch.</param>
        /// <returns>true if raycast hit is detected , otherwise false</returns>
		private bool RayCast (Vector3 clickPosition)
		{
            if (!IsValidRayCast(clickPosition))
            {
                return false;
            }

			//Get the click position in the world space
			tempClickPosition = Camera.main.ScreenToWorldPoint (clickPosition);
			tempClickPosition.z = 0;

			//Ray cast hit test
			RaycastHit2D tempRayCastHit2D = Physics2D.Raycast (tempClickPosition, Vector2.zero);
			Collider2D tempCollider2D = tempRayCastHit2D.collider;
				
			if (tempCollider2D != null) {
				PathObject pathObject = tempCollider2D.GetComponent<PathObject> ();

				//When a ray hit a path object
				if (pathObject != null) {
					pathObject.OnHit (PathObject.DestroyMode.SMASH, destroyEffects, true);
				}

                return true;
			}

            return false;
		}

        /// <summary>
        /// Whether the raycast is valid or not
        /// </summary>
        /// <param name="pos">Click/Touch Position</param>
        /// <returns>true if valid , otherwise false</returns>
        private bool IsValidRayCast(Vector3 pos)
        {
            if (!isRunning)
            {
                return false;
            }

            //Skop next,  if the distance between the current click postion and the previous click position less than 0.05 
            if (Vector2.Distance(tempClickPosition, Camera.main.ScreenToWorldPoint(pos)) < 0.05f)
            {
                return false;
            }

            return true;
        }

		/// <summary>
		/// Go to the Next level.
		/// </summary>
		public void NextLevel ()
		{
			if (Level.SelectedLevelID > 0 && Level.SelectedLevelID < LevelsManager.instance.levels.Count) {
				//Get the next level and check if it's locked , then do not load the next level
				if (LevelsManager.instance.levels [Level.SelectedLevelID].isLocked) {
					//Play locked sound effect
					AudioSources.instance.PlayLockedSFX ();
					//Skip the next
					return;
				}
				Level.SelectedLevelID++;
				UIEvents.instance.LoadGameScene ();
			} else {
				UIEvents.instance.LoadLevelsScene ();
			}
		}

		/// <summary>
		/// Go to the previous level.
		/// </summary>
		public void PreviousLevel ()
		{
			if (Level.SelectedLevelID > 1 && Level.SelectedLevelID <= LevelsManager.instance.levels.Count) {
				Level.SelectedLevelID--;
				UIEvents.instance.LoadGameScene ();
			}
		}

		/// <summary>
		/// Check Wheter the level is completed or not.
		/// </summary>
		public void CheckLevelComplete ()
		{
			if (!GameManager.instance.isRunning) {
				return;
			}

			bool completed = true;

			for (int i = 0; i < targetsScore.Count; i++) {
				if (targetsScore [i].GetValue () != targetsScore [i].GetMaxValue ()) {
					completed = false;
					break;
				}
			}

			if (completed) {

				//Disable the trails of the cursor/touch
				Cursor.instance.DisableTrails ();

				//Pause the game manager
				Pause ();

				//Save the data of the level
				LevelsManager.instance.GetCurrentLevel ().score = levelScore;
				LevelsManager.instance.GetCurrentLevel ().starsNumber = CommonUtil.IntNumberToLevelStarsNumberEnum (Progress.instance.starsCount);
				LevelsManager.instance.levels [Level.SelectedLevelID-1].isLastRreached = false;

				DataManager.SaveLevelScore (LevelsManager.instance.GetCurrentLevel ().ID, levelScore);
				DataManager.SaveLevelStars (LevelsManager.instance.GetCurrentLevel ().ID, LevelsManager.instance.GetCurrentLevel ().starsNumber);

				//Unlock the next level
				DataManager.SaveLevelLockedStatus (LevelsManager.instance.GetCurrentLevel ().ID + 1, false);

				if (Level.SelectedLevelID > -1 && Level.SelectedLevelID < LevelsManager.instance.levels.Count) {
					if (LevelsManager.instance.levels [Level.SelectedLevelID].isLocked)
						LevelsManager.instance.levels [Level.SelectedLevelID].isLastRreached = true;
					LevelsManager.instance.levels [Level.SelectedLevelID].isLocked = false;
				}

				//On All Levels completed, show levels completed dialog on load Levels scene
				if (DataManager.GetLevelsCompletedValue () == false && Level.SelectedLevelID == LevelsManager.instance.levels.Count) {
					//Turn flag on
					Levels.levelsCompleted = true;
				}

				//Setup the attributes of the win dialog
				WinDialog.instance.SetLevelTitle ("Level " + Level.SelectedLevelID);
				WinDialog.instance.SetScore (levelScore);
				WinDialog.instance.SetStars (LevelsManager.instance.GetCurrentLevel ().starsNumber);
					
				//Show win dialog
				WinDialog.instance.Invoke ("Show", 1);

				Debug.Log ("Level is completed");
			}
		}

		/// <summary>
		/// On user becomes idle event.
		/// </summary>
		private void OnUserBecomesIdle ()
		{
			//Enable target hint
			targetHint.Enable ();
		}

		/// <summary>
		/// On user becomes active event.
		/// </summary>
		private void OnUserBecomesActive ()
		{
			//Disable target hint
			targetHint.Disable ();
		}

        
        /// <summary>
        /// Create new Finger Print
        /// </summary>
        private void CreateFingerPrint(Vector3 pos)
        {
            if (PrefabsManager.instance.fingerPrintPrefab != null)
            {
                GameObject fingerPrint = Instantiate(PrefabsManager.instance.fingerPrintPrefab, Vector3.zero, Quaternion.identity) as GameObject;
                if (fingerPrintParent != null)
                    fingerPrint.transform.SetParent(fingerPrintParent);

                Vector3 tempPos = Camera.main.ScreenToWorldPoint(pos);
                tempPos.z = 0;
                fingerPrint.transform.position = tempPos;
                fingerPrint.transform.localScale = Vector3.one;
                AudioSources.instance.PlayFingerPrintSFX();
            }
        }

		/// <summary>
		/// Pause the game.
		/// </summary>
		public void Pause ()
		{
			if (!isRunning) {
				return;
			}

			mouseClickDown = false;

			if (Timer.instance != null)
				Timer.instance.Pause ();
			isRunning = false;
		}

		/// <summary>
		/// Pause the gamse.
		/// </summary>
		public void Resume ()
		{
			if (Timer.instance != null)
				Timer.instance.Resume ();
			SetRunning (true);
		}

		/// <summary>
		/// Set the level title text.
		/// </summary>
		private void SetLevelTitleText ()
		{
			if (levelText == null)
				return;

			levelText.text = "LEVEL " + CommonUtil.IntToString (Level.SelectedLevelID);
		}

		/// <summary>
		/// Set level score text value.
		/// </summary>
		/// <param name="value">Value.</param>
		public void SetScoreText (int value)
		{
			if (scoreText == null)
				return;
			scoreText.text = "SCORE : " + value.ToString ();
		}

		/// <summary>
		/// Set level lives fill value.
		/// </summary>
		/// <param name="value">Value.</param>
		public void SetLivesFillValue (int value)
		{
			if (livesImage == null)
				return;
			livesImage.fillAmount = currentLives * 1.0f / LevelsManager.instance.GetCurrentLevel ().lives;
		}

		/// <summary>
		/// On Time out.
		/// </summary>
		public void OnTimeOut ()
		{
			//show time out dialog after 1 second
			Pause ();
			UIEvents.instance.Invoke ("ShowTimeOutDialog", 1);
		}

		/// <summary>
		/// On lives equals zero event.
		/// </summary>
		public void OnLivesEqualsZero ()
		{
			Pause ();
			UIEvents.instance.ShowNoLivesDialog ();
			Debug.Log ("You have no lives");
		}

		/// <summary>
		/// Determine whether the game manager is running specially RayCast or not.
		/// </summary>
		public bool IsRunning ()
		{
			return isRunning;
		}

		/// <summary>
		/// Set isRunning flag value.
		/// </summary>
		public void SetRunning (bool isRunning)
		{
			this.isRunning = isRunning;
		}

		/// <summary>
		/// Mobile vibration.
		/// </summary>
		public static void Vibrate ()
		{
			#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TIZEN
			if (!Settings.vibrationEnabled) {
				return;
			}
			Handheld.Vibrate ();
			#endif
		}
	}
}